﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace RG.Common.WPF.Commands
{
    /// <summary>
    /// <para>Базовый класс для биндинга комманд. При каждом вызове CanExecute или Execute проверяет 
    /// изменился ли DataContext (сравнивая с запомненным). Если изменился - то настраивает себя
    /// (через вызов OnSetLastContext, который должен быть перекрыт в потомках) на вызов команд 
    /// нового DataContext</para>
    /// 
    /// <para>При этом перед вызовом Execute дергает принудительно обновление источника привязки 
    /// для TextBox если он находится в фокусе 
    /// (подробнее см. <see cref="BaseCommandBinding.UpdateTextBoxSource"/>)</para>
    /// </summary>
    public abstract class BaseCommandBinding : CommandBinding
    {
        private object lastContext;
        private object LastContext
        {
            get { return lastContext; }
            set
            {
                lastContext = value;
                OnSetLastContext(lastContext);
            }
        }

        protected abstract void OnSetLastContext(object lastContext);
        protected abstract void InternalCanExecute(object obj, CanExecuteRoutedEventArgs args);
        protected abstract void InternalExecute(object obj, ExecutedRoutedEventArgs args);

        private void CheckLastContext(object obj)
        {
            var fe = obj as FrameworkElement;

            if ((fe == null) || (fe.DataContext == null))
            {
                LastContext = null;
                return;
            }

            if (LastContext == fe.DataContext) return;

            LastContext = fe.DataContext;
        }


        protected BaseCommandBinding()
        {
            Executed += ExecuteHandler;
            CanExecute += CanExecuteHandler;
        }

        /// <summary>
        /// Извращения на тему http://www.codeproject.com/KB/WPF/commandgroup.aspx?display=Print 
        /// TextBox при выполнении комманды не теряет фокус, соотв. не обновляет binding
        /// </summary>
        private static void UpdateTextBoxSource()
        {
            var focusedTextBox = Keyboard.FocusedElement as TextBox;
            if (focusedTextBox != null)
            {
                var textBindingExpr = focusedTextBox.GetBindingExpression(TextBox.TextProperty);
                if (textBindingExpr != null)

                    textBindingExpr.UpdateSource();
            }
        }

        public void ExecuteHandler(object obj, ExecutedRoutedEventArgs args)
        {
            UpdateTextBoxSource();

            CheckLastContext(obj);

            InternalExecute(obj, args);
        }

        public void CanExecuteHandler(object obj, CanExecuteRoutedEventArgs args)
        {
            CheckLastContext(obj);

            InternalCanExecute(obj, args);
        }

    }
}
